﻿using System;
using System.Collections.Generic;
using System.Text;
using Pixysoft.Framework.Noebe;
using System.Data;

namespace Pixysoft.Weblications.Sango
{
    class NoebeTransaction : INoebeTransaction
    {
        INoebeCommand command = null;

        System.Data.IsolationLevel level = IsolationLevel.ReadCommitted;

        List<TransactionObject> transactionlist = new List<TransactionObject>();

        public NoebeTransaction(INoebeCommand command)
        {
            this.command = command;
        }

        #region ISession 成员

        public int AutoInsert(System.Data.DataRow row)
        {
            return AutoInsert(RowToTable(row));
        }

        public int AutoInsert(System.Data.DataTable table)
        {
            this.transactionlist.Add(new TransactionObject(TransactionObject.AUTOINSERT, table));

            return -1;
        }

        public void CascadeDelete(System.Data.DataRow row)
        {
            CascadeDelete(RowToTable(row));
        }

        public void CascadeDelete(System.Data.DataTable table)
        {
            this.transactionlist.Add(new TransactionObject(TransactionObject.CASCADEDELETE, table));
        }

        public int Delete(System.Data.DataRow row)
        {
            return Delete(RowToTable(row));
        }

        public int Delete(System.Data.DataTable table)
        {
            this.transactionlist.Add(new TransactionObject(TransactionObject.DELETE, table));

            return -1;
        }

        public int Insert(System.Data.DataRow row)
        {
            return Insert(RowToTable(row));
        }

        public int Insert(System.Data.DataTable table)
        {
            this.transactionlist.Add(new TransactionObject(TransactionObject.INSERT, table));

            return -1;
        }

        public int Update(System.Data.DataRow row)
        {
            return Update(RowToTable(row));
        }

        public int Update(System.Data.DataTable table)
        {
            this.transactionlist.Add(new TransactionObject(TransactionObject.UPDATE, table));

            return -1;
        }

        #endregion

        #region IDisposable 成员

        public void Dispose()
        {
            this.transactionlist.Clear();
            command.Dispose();
        }

        #endregion

        #region INoebeTransaction 成员

        public bool IsOpen
        {
            get { return command.IsOpen; }
        }

        public void Begin()
        {
            Begin(IsolationLevel.ReadCommitted);
        }

        public void Begin(System.Data.IsolationLevel level)
        {
            this.level = level;
        }

        public void Begin(NoebeTransactionArea transactionArea)
        {
            Begin(IsolationLevel.ReadCommitted, transactionArea);
        }

        public void Begin(System.Data.IsolationLevel level, NoebeTransactionArea transactionArea)
        {
            Begin(level);

            try
            {
                transactionArea();

                if (this.IsOpen)
                {
                    Commit();
                }
            }
            catch (Exception ex)
            {
                Rollback();

                throw ex;
            }
        }

        public void Commit()
        {
            if (transactionlist.Count == 0)
                return;

            transactionlist.Sort();

            command.Begin(level);

            try
            {
                foreach (TransactionObject target in transactionlist)
                {
                    if (target.Status == TransactionObject.NONE)
                        continue;

                    else if (target.Status == TransactionObject.AUTOINSERT)
                    {
                        command.AutoInsert(target.Target);
                    }

                    else if (target.Status == TransactionObject.INSERT)
                    {
                        command.Insert(target.Target);
                    }

                    else if (target.Status == TransactionObject.UPDATE)
                    {
                        command.Update(target.Target);
                    }

                    else if (target.Status == TransactionObject.DELETE)
                    {
                        command.Delete(target.Target);
                    }

                    else if (target.Status == TransactionObject.CASCADEDELETE)
                    {
                        command.CascadeDelete(target.Target);
                    }
                }

                command.Commit();
            }
            catch (Exception ex)
            {
                LoggerHelper.Fatal(ex);

                try
                {
                    command.Rollback();
                }
                catch
                {
                }

                throw ex;
            }
        }

        public void Rollback()
        {
            command.Rollback();
        }

        #endregion

        private DataTable RowToTable(DataRow row)
        {
            DataTable cloneTable = row.Table.Clone();
            DataRow cloneRow = cloneTable.NewRow();

            foreach (DataColumn column in row.Table.Columns)
            {
                cloneRow[column.ColumnName] = row[column.ColumnName];
            }
            cloneTable.Rows.Add(cloneRow);
            return cloneTable;
        }

        class TransactionObject : IComparable<TransactionObject>
        {
            public static int NONE = 0;
            public static int AUTOINSERT = 1;
            public static int INSERT = 2;
            public static int UPDATE = 4;
            public static int DELETE = 8;
            public static int CASCADEDELETE = 16;

            int status = NONE;
            DataTable target = null;

            public TransactionObject(int status, DataTable target)
            {
                this.status = status;
                this.target = target;
            }

            public int Status
            {
                get
                {
                    return status;
                }
            }

            public DataTable Target
            {
                get
                {
                    return target;
                }
            }

            public int CompareTo(TransactionObject other)
            {
                DataTable localTable = this.Target;

                DataTable targetTable = other.Target;

                int compareResult = localTable.TableName.CompareTo(targetTable.TableName);

                return compareResult;
            }
        }
    }
}
